/*
@Time : 2020/6/20 11:15
@Author : yaodongpo
@File : gen
@Software: GoLand
*/
package gen

import (
	"bytes"
	"fmt"
	"git.9885.net/lib/go/common/db"
	"github.com/astaxie/beego"
	"strings"
	"text/template"
	"time"
)

//数据库数据类型与model数据类型的映射
var typeMappingMysql = map[string]string{
	"int":                "int", // int signed
	"integer":            "int",
	"tinyint":            "int8",
	"smallint":           "int16",
	"mediumint":          "int32",
	"bigint":             "int64",
	"int unsigned":       "uint", // int unsigned
	"integer unsigned":   "uint",
	"tinyint unsigned":   "uint8",
	"smallint unsigned":  "uint16",
	"mediumint unsigned": "uint32",
	"bigint unsigned":    "uint64",
	"bit":                "uint64",
	"bool":               "bool",   // boolean
	"enum":               "string", // enum
	"set":                "string", // set
	"varchar":            "string", // string & text
	"char":               "string",
	"tinytext":           "string",
	"mediumtext":         "string",
	"text":               "string",
	"longtext":           "string",
	"blob":               "string", // blob
	"tinyblob":           "string",
	"mediumblob":         "string",
	"longblob":           "string",
	"date":               "time.Time", // time
	"datetime":           "time.Time",
	"timestamp":          "time.Time",
	"time":               "time.Time",
	"float":              "float32", // float & decimal
	"double":             "float64",
	"decimal":            "float64",
	"binary":             "string", // binary
	"varbinary":          "string",
	"year":               "int16",
}

//表定义
type Table struct {
	TableId             int32  `json:"table_id"`

	TableName           string `json:"table_name"`
	TableComment        string `json:"table_comment"`

	ClassName           string `json:"class_name"`
	TplCategory         string `json:"tpl_category"`
	PackageName         string `json:"package_name"`
	FunctionAuthor      string `json:"function_author"`
	Remark              string `json:"remark"`

	IsLogicalDelete     string `json:"is_logical_delete"`
	LogicalDelete       string `json:"logical_delete"`
	LogicalDeleteColumn string `json:"logical_delete_column"`
}

//列
type Column struct {
	ColumnId              int64  `json:"column_id"`
	TableId               int64 `json:"table_id"`

	ColumnName    string `json:"column_name"`
	ColumnComment string `json:"column_comment"`
	ColumnType    string `json:"column_type"`
	DataType      string `json:"data_type"`

	GoType                string `json:"go_type"`
	GoField               string `json:"go_field"`
	JsonField             string `json:"json_field"`
	
	IsPk                  string `json:"is_pk"`
}

func InitGenSet(tNames string) bool {
	//tableNames:=strings.Split(tNames, ",")
	//tNames = strings.Join(tableNames,"','")
	// 查询数据表
	sqlStr := "select table_name,table_comment from TABLES where TABLE_SCHEMA='"+beego.AppConfig.String("db.name")+"' and TABLE_NAME in ("+ tNames +");"
	tables := GetListBySqlAndConnStr(sqlStr)

	for _, table := range tables {
		fmt.Println(table)
		table["class_name"] = CamelCase(table["table_name"])
		table["tpl_category"] = "curd"
		table["package_name"] = "models"
		table["function_author"] = "gen"
		table["is_logical_delete"] = "0"

		nMap := ConvertMap(table)
		nMap["created_at"] = time.Now().UTC()
		nMap["updated_at"] = time.Now().UTC()
		// 这里循环插入，不过只在开发环境用得上，不影响线上效率
		rs, err := db.Session().InsertInto("gen_tables").Map(nMap).Exec()
		if err != nil {
			CheckErr(err)
		} else {
			fmt.Println(rs.LastInsertId())
			tableId, _ := rs.LastInsertId()
			InitColumnSet(table["table_name"], tableId)
		}
	}
	return true
}

func InitColumnSet(tName string, tableId int64) {
	sqlStr := "select column_name, data_type, column_type, column_comment, column_key from COLUMNS where TABLE_SCHEMA='"+beego.AppConfig.String("db.name")+"' and TABLE_NAME='" + tName + "';"
	rows := GetListBySqlAndConnStr(sqlStr)
	for _, v := range rows{
		if v["column_key"] == "PRI"{
			v["is_pk"] = "1"
		}else {
			v["is_pk"] = "0"
		}
		rowMap := ConvertMap(v)
		rowMap["table_id"] = tableId
		rowMap["go_field"] = CamelCase(v["column_name"])
		rowMap["go_type"] = typeMappingMysql[v["data_type"]]
		rowMap["json_field"] = v["column_name"]
		rowMap["created_at"] = time.Now().UTC()
		rowMap["updated_at"] = time.Now().UTC()
		// 删除无用键值，否则dbr会报错
		delete(rowMap, "column_key")
		delete(rowMap, "data_type")
		fmt.Println(rowMap)
		rs, err:= db.Session().InsertInto("gen_table_columns").Map(rowMap).Exec()
		if err != nil {
			CheckErr(err)
		} else {
			fmt.Println(rs.LastInsertId())
		}
	}
}

// 下划线命名转换驼峰命名
func CamelCase(in string) string {
	tokens := strings.Split(in, "_")
	for i := range tokens {
		tokens[i] = strings.Title(strings.Trim(tokens[i], " "))
	}
	return strings.Join(tokens, "")
}

func ConvertMap(table map[string]string) map[string]interface{} {
	nMap := make(map[string]interface{})
	for k,v := range table {
		nMap[k] = interface{}(v)
	}
	return nMap
}

func TemplateRender(tableName string) (map[string]string, error) {
	// 获取gen_tables和gen_table_columns设置，是否需要导入time.Time，pk
	ret := map[string]string{
		"model":"",
		"controller":"",
	}
	genTableSet := new(Table)
	err := db.Session().Select("*").From("gen_tables").Where("table_name = ?", tableName).LoadOne(&genTableSet)
	if err!=nil{
		CheckErr(err)
	}
	var genTableColumns []Column
	_, err = db.Session().Select("*").From("gen_table_columns").Where("table_id = ?", genTableSet.TableId).Load(&genTableColumns)
	if err!= nil {
		CheckErr(err)
	}
	Time := time.Now().Format("2006-01-02 15:04:05")
	pk := ""
	timePkg := ""
	for _, value := range genTableColumns{
		if value.IsPk == "1"{
			pk = value.ColumnName
			continue
		}
		if value.GoType == "time.Time"{
			timePkg = "time"
		}
	}
	type Content map[string]interface{}
	content := Content{
		"timePkg": timePkg,
		"pk": pk,
		"Time": Time,
		"TB":genTableSet,
		"Columns": genTableColumns,
		"appName":beego.AppConfig.String("appname"),
	}
	modelT, err := template.ParseFiles("gen/template/model.go.template")
	CheckErr(err)
	controllerT, err := template.ParseFiles("gen/template/api.go.template")
	CheckErr(err)
	var modelBytes, controllerBytes bytes.Buffer
	err = modelT.Execute(&modelBytes, content)
	CheckErr(err)
	err = controllerT.Execute(&controllerBytes, content)
	CheckErr(err)
	ret["model"] = modelBytes.String()
	ret["controller"] = controllerBytes.String()
	return ret, nil
}



